package com.qingfeng.medical.biz.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qingfeng.medical.base.handler.GeneralAuthException;
import com.qingfeng.medical.base.result.ResultCodeEnum;
import com.qingfeng.medical.biz.auth.domain.entity.SysUser;
import com.qingfeng.medical.biz.auth.service.SysUserService;
import com.qingfeng.medical.biz.hosp.domain.entity.HospDepartment;
import com.qingfeng.medical.biz.hosp.domain.entity.Schedule;
import com.qingfeng.medical.biz.hosp.service.HospDepartmentService;
import com.qingfeng.medical.biz.hosp.service.ScheduleService;
import com.qingfeng.medical.biz.information.domain.entity.DoctorInfo;
import com.qingfeng.medical.biz.information.domain.entity.HospitalInfo;
import com.qingfeng.medical.biz.information.domain.entity.Patient;
import com.qingfeng.medical.biz.information.service.DoctorInfoService;
import com.qingfeng.medical.biz.information.service.HospitalInfoService;
import com.qingfeng.medical.biz.information.service.PatientService;
import com.qingfeng.medical.biz.order.dao.OrderInfoMapper;
import com.qingfeng.medical.biz.order.domain.entity.OrderInfo;
import com.qingfeng.medical.biz.order.domain.entity.VisitRecord;
import com.qingfeng.medical.biz.order.domain.enums.OrderStatusEnum;
import com.qingfeng.medical.biz.order.domain.enums.VisitStatusEnum;
import com.qingfeng.medical.biz.order.domain.ro.OrderInfoRo;
import com.qingfeng.medical.biz.order.domain.vo.OrderInfoVo;
import com.qingfeng.medical.biz.order.service.OrderInfoService;
import com.qingfeng.medical.biz.order.service.VisitRecordService;
import com.qingfeng.medical.biz.order.service.WeixinService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 订单表
 *
 * @author 清风学Java
 * @date 2023-04-28 17:10:43
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    private PatientService patientService;
    @Autowired
    private HospDepartmentService hospDepartmentService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private DoctorInfoService doctorInfoService;
    @Autowired
    private HospitalInfoService hospitalInfoService;
    @Autowired
    private WeixinService weixinService;
    @Autowired
    private VisitRecordService visitRecordService;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 订单分页列表
     *
     * @param orderInfoPage
     * @param patientId
     * @param orderStatus
     * @return
     */
    @Override
    public IPage<OrderInfo> orderInfoList(Page<OrderInfo> orderInfoPage,
                                          Long patientId,
                                          OrderStatusEnum orderStatus,
                                          Long userId) {
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getUserId, userId)
                .orderByDesc(OrderInfo::getCreateTime)
                .orderByDesc(OrderInfo::getReserveDate);
        if (ObjectUtil.isNotNull(patientId)) {
            queryWrapper.eq(OrderInfo::getPatientId, patientId);
        }
        if (ObjectUtil.isNotNull(orderStatus)) {
            queryWrapper.eq(OrderInfo::getOrderStatus, orderStatus);
        }
        return baseMapper.selectPage(orderInfoPage, queryWrapper);
    }

    /**
     * 查询订单列表
     *
     * @param orderInfoPage
     * @param orderStatus
     * @param depName
     * @param patientName
     * @param orderDate
     * @return
     */
    @Override
    public IPage<OrderInfo> orderList(Page<OrderInfo> orderInfoPage,
                                      OrderStatusEnum orderStatus,
                                      String depName,
                                      String patientName,
                                      LocalDate orderDate) {
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<OrderInfo>()
                .orderByAsc(OrderInfo::getOrderStatus)
                .orderByAsc(OrderInfo::getReserveDate);
        if (StrUtil.isNotBlank(depName)) {
            queryWrapper.eq(OrderInfo::getDepName, depName);
        }
        if (StrUtil.isNotBlank(patientName)) {
            queryWrapper.eq(OrderInfo::getPatientName, patientName);
        }
        if (ObjectUtil.isNotNull(orderStatus)) {
            queryWrapper.eq(OrderInfo::getOrderStatus, orderStatus);
        }
        if (ObjectUtil.isNotNull(orderDate)) {
            queryWrapper.eq(OrderInfo::getReserveDate, orderDate);
        }

        return baseMapper.selectPage(orderInfoPage, queryWrapper);
    }

    /**
     * 保存订单表信息
     *
     * @param scheduleId
     * @param patientId
     */
    @Override
    public Long saveOrders(Long scheduleId, Long patientId, Long userId) {
        // 使用Redisson做分布式锁  对给个排班订单信息中做加锁操作
        RLock lock = redissonClient.getLock(scheduleId.toString());
        try {
            // 尝试加锁，最多等待100秒 上锁以后10秒解锁
            boolean res = lock.tryLock(100, 3, TimeUnit.SECONDS);
            if (res) {
                // 查询排班信息
                Schedule schedule = scheduleService.getById(scheduleId);

                // 一个人一天只能预约一个科室门诊的号
                List<OrderInfo> orderInfoList = baseMapper.selectList(
                        new LambdaQueryWrapper<OrderInfo>()
                                .eq(OrderInfo::getReserveDate, schedule.getWorkDate())
                                .eq(OrderInfo::getPatientId, patientId)
                                .notIn(OrderInfo::getOrderStatus, Arrays.asList(
                                        OrderStatusEnum.UNSUBSCRIBE,
                                        OrderStatusEnum.DISCARD
                                ))
                );
                if (CollUtil.isNotEmpty(orderInfoList)) {
                    throw new GeneralAuthException(ResultCodeEnum.FAIL.getCode(), "不能重复预约！！！");
                }

                // 查询就诊人信息
                Patient patient = patientService.getById(patientId);
                // 根据排班查询科室门诊信息
                HospDepartment hospDepartment = hospDepartmentService.getById(schedule.getDepId());
                // 查询医生基础信息
                SysUser user = sysUserService.getById(schedule.getDoctorId());
                // 查询医生详细信息
                DoctorInfo doctorInfo = doctorInfoService.getOne(
                        new LambdaQueryWrapper<DoctorInfo>()
                                .eq(DoctorInfo::getUserId, schedule.getDoctorId())
                );

                // 修改订单数量
                schedule.setAvailableNum(schedule.getAvailableNum() - 1);
                schedule.setReservationNum(schedule.getReservationNum() + 1);
                scheduleService.updateById(schedule);

                OrderInfo orderInfo = OrderInfo.builder()
                        .depId(hospDepartment.getId())
                        .userId(userId)
                        .outTradeNo(System.currentTimeMillis() + "" + new Random().nextInt(100))
                        .depName(hospDepartment.getDepName())
                        .patientName(hospDepartment.getPatientName())
                        .scheduleId(schedule.getId())
                        .doctorId(user.getId())
                        .doctorName(user.getName())
                        .position(doctorInfo.getPosition().getMsg())
                        .reserveDate(schedule.getWorkDate())
                        .patientId(patient.getId())
                        .patientUserName(patient.getName())
                        .patientPhone(patient.getPhone())
                        .number(schedule.getReservationNum())
                        .fetchTime(new SimpleDateFormat("yyyy-MM-dd").format(schedule.getWorkDate()) + "  9:00前")
                        .fetchAddress("一层114窗口")
                        .amount(schedule.getPrice())
                        .orderStatus(OrderStatusEnum.INIT)
                        .build();
                baseMapper.insert(orderInfo);

                // 新增就诊信息
                visitRecordService.save(
                        VisitRecord.builder()
                                .orderId(orderInfo.getId())
                                .visitStatus(VisitStatusEnum.INIT)
                                .build()
                );

                return orderInfo.getId();
            } else {
                throw new GeneralAuthException(ResultCodeEnum.FAIL.getCode(), "系统繁忙，请稍后再试！！！");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new GeneralAuthException(ResultCodeEnum.FAIL.getCode(), e.getMessage());
        } finally {
            lock.unlock();
        }
    }

    /**
     * 取消预约
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = GeneralAuthException.class)
    public void cancelOrder(Long id) throws ParseException {
        // 医院信息
        HospitalInfo hospitalInfo = hospitalInfoService.list().get(0);
        // 查询是否支付，支付查看是否满足退款条件
        OrderInfo orderInfo = baseMapper.selectById(id);
        // 判断取消预约的时间是否是合规的   就诊前一天挂号时间内支持退款
        // 比较当前时间  当前日期在挂号日期之前
        if (new Date().before(orderInfo.getReserveDate())
                && new Date().before(
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                        .parse(
                                new SimpleDateFormat("yyyy-MM-dd")
                                        .format(orderInfo.getReserveDate())
                                        + " "
                                        + hospitalInfo.getBookingRule()
                                        .getReleaseEndTime()
                                        + ":00"
                        ))) {
            if (OrderStatusEnum.SUCCESS.equals(orderInfo.getOrderStatus())) {
                // 已支付，判断是否满足退款条件
                boolean isRefund = weixinService.refund(id);
                if (!isRefund) {
                    throw new GeneralAuthException(ResultCodeEnum.FAIL.getCode(), "取消订单失败");
                }
            }
            // 支持退号，修改预约订单状态
            orderInfo.setOrderStatus(OrderStatusEnum.UNSUBSCRIBE);
            orderInfo.setQuitTime(LocalDateTime.now());
            baseMapper.updateById(orderInfo);

            // 修改就诊状态
            VisitRecord visitRecord = visitRecordService.getOne(
                    new LambdaQueryWrapper<VisitRecord>()
                            .eq(VisitRecord::getOrderId, orderInfo.getId())
            );
            visitRecord.setVisitStatus(VisitStatusEnum.UNSUBSCRIBE);
            visitRecordService.updateById(visitRecord);

            // 恢复预约排班的个数
            Schedule schedule = scheduleService.getById(orderInfo.getScheduleId());
            schedule.setAvailableNum(schedule.getAvailableNum() + 1);
            schedule.setReservationNum(schedule.getReservationNum() - 1);
            scheduleService.updateById(schedule);

        } else {
            throw new GeneralAuthException(ResultCodeEnum.FAIL.getCode(), "已过取消预约时间，不支持取消预约！！！");
        }
    }

    /**
     * 查询订单信息
     *
     * @param orderInfoPage
     * @param depId
     * @param orderDate
     * @return
     */
    @Override
    public OrderInfoVo findDoctorOrderInfoList(Page<OrderInfo> orderInfoPage, Long depId, LocalDate orderDate) {
        // 查询所有订单
        Integer count = baseMapper.selectCount(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getDepId, depId)
                        .eq(OrderInfo::getReserveDate, orderDate)
                        .in(OrderInfo::getOrderStatus, Arrays.asList(
                                OrderStatusEnum.INIT,
                                OrderStatusEnum.SUCCESS)
                        )
        );

        if (count == 0) {
            return OrderInfoVo.builder()
                    .pageSize(orderInfoPage.getPages())
                    .limit(orderInfoPage.getSize())
                    .total(orderInfoPage.getTotal())
                    .orderInfoRoList(Collections.emptyList())
                    .build();
        }

        List<OrderInfoRo> orderInfoRoList = baseMapper.selectOrderInfoList(orderInfoPage, depId, orderDate);

        return OrderInfoVo.builder()
                .pageSize(orderInfoPage.getPages())
                .limit(orderInfoPage.getSize())
                .total(orderInfoPage.getTotal())
                .orderInfoRoList(orderInfoRoList)
                .build();

    }
}